using System;
using MLSharp.Classification;
using weka.classifiers.trees;
using weka.core;

namespace MLSharp.Weka.Classifiers
{
	/// <summary>
	/// A <see cref="IClassifierFactory"/> that trains random
	/// forest based classifiers.
	/// </summary>
	public class RandomForestClassifierFactory : IClassifierFactory
	{
		#region Public Properties

		/// <summary>
		/// The options used to build a new Random Forest.
		/// </summary>
		public RandomForestOptions Options { get; private set; }

		#endregion

		#region Public Methods

		/// <summary>
		/// Trains a random forest.
		/// </summary>
		/// <param name="trainingData"></param>
		/// <returns></returns>
		public IRandomForestClassifier BuildRandomForestClassifier(IDataSet trainingData)
		{
			Instances instances = trainingData.ToInstances(true);

			RandomForest forest = new RandomForest();
			forest.setSeed(Options.RandomSeed);
			forest.setNumTrees(Options.NumTrees);
			//If the number of features is 0, automatically compute the number of features to use.
			forest.setNumFeatures(Options.NumFeatures != 0 ? Options.NumFeatures : (int)Math.Log10(instances.numAttributes()) + 1);
			forest.setMaxDepth(Options.MaxTreeDepth);

			forest.buildClassifier(instances);

			return new RandomForestClassifier(forest);
		}

		#endregion

		#region Public Constructors

		/// <summary>
		/// Initializes the factory for use.
		/// </summary>
		public RandomForestClassifierFactory()
		{
			Options = new RandomForestOptions();
		}

		#endregion

		#region Implementation of IClassifierFactory

		/// <summary>
		/// Builds a classifier to predict the specified attribute using the 
		/// specified training data.
		/// </summary>
		/// <param name="trainingData">The training data.</param>
		/// <returns>A classifier.</returns>
		IClassifier IClassifierFactory.BuildClassifier(IDataSet trainingData)
		{
			return BuildRandomForestClassifier(trainingData);
		}

		/// <summary>
		/// The options object for the factory.
		/// </summary>
		/// <remarks>
		/// Implementors should implement this property explicitly and expose 
		/// a strongly-typed options object instead.  
		/// </remarks>
		object IClassifierFactory.Options
		{
			get { return Options; }
		}

		#endregion
	}
}
